package org.check.Util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.Format;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.check.VO.DBConnVO;
import org.check.VO.Server;
import org.jdom2.DocType;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.adapters.DOMAdapter;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.XMLOutputter;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class XmlParser {

	public static List<DBConnVO> generateDBVOList(String filePath) {
		List<DBConnVO> reslist = new ArrayList<DBConnVO>();
		try {
			org.jdom2.Document jdomDoc = useDOMParser(filePath);
			Element root = jdomDoc.getRootElement();
			List<Element> connListElements = root.getChildren("university");
			for (Element ele : connListElements) { // ele: university
				List<Element> children = ele.getChildren();
				Boolean isRoute = false;
				DBConnVO vo = new DBConnVO();
				for (Element e : children) { // e: name, dbtype, ... , isRoute
					String attrName = e.getName();
					String value = e.getText();
					Class cls = DBConnVO.class;
					Method[] methods = cls.getDeclaredMethods();
					if (attrName.equalsIgnoreCase("isRoute")) {
						isRoute = Boolean.parseBoolean(e.getAttribute("value").getValue());
						if (isRoute) {
							List<Element> routeChildren = e.getChildren();
							for (Element routeEle : routeChildren) { // routeEle:
																		// servers
								List<Element> servers = routeEle.getChildren();
								for (Element server : servers) { // server:
																	// server
									String serversAttrName = server.getName();
									Server sVo = new Server();
									List<Element> serverEles = server.getChildren();
									for (Element serverEle : serverEles) { // serverEle:
																			// host,
																			// user,
																			// pwd,
																			// port
										String serverAttrName = serverEle.getName();
										Object serverValue = serverEle.getText();
										String setMethodName = "set" + serverAttrName;
										Class scls = Server.class;
										Method[] smethods = scls.getDeclaredMethods();
										for (int j = 0; j < smethods.length; j++) {
											if (smethods[j].getName().equalsIgnoreCase(setMethodName))
												setMethodName = smethods[j].getName();
										}
										Method setMethod;
										setMethod = sVo.getClass().getMethod(setMethodName, serverValue.getClass());
										setMethod.invoke(sVo, serverValue);
									}
									String setMethodName = "add" + serversAttrName;
									for (int j = 0; j < methods.length; j++) {
										if (methods[j].getName().equalsIgnoreCase(setMethodName))
											setMethodName = methods[j].getName();
									}
									Method setMethod;
									setMethod = vo.getClass().getMethod(setMethodName, sVo.getClass());
									setMethod.invoke(vo, sVo);
								}
							}
						}
						String setMethodName = "set" + attrName;
						for (int j = 0; j < methods.length; j++) {
							if (methods[j].getName().equalsIgnoreCase(setMethodName))
								setMethodName = methods[j].getName();
						}
						Method setMethod;
						setMethod = vo.getClass().getMethod(setMethodName, isRoute.getClass());
						setMethod.invoke(vo, isRoute);

					} else {
						String setMethodName = "set" + attrName;
						for (int j = 0; j < methods.length; j++) {
							if (methods[j].getName().equalsIgnoreCase(setMethodName))
								setMethodName = methods[j].getName();
						}
						Method setMethod;
						setMethod = vo.getClass().getMethod(setMethodName, value.getClass());
						setMethod.invoke(vo, value);
					}
				}
				reslist.add(vo);
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return reslist;
	}
	
	public static void updateFromDBVOList(String filePath, List<DBConnVO> voList) {
		try {
			org.jdom2.Document jdomDoc = useDOMParser("conns_config.xml"); //to be replaced
			
			Element root = jdomDoc.getRootElement();
			//List<Element> connListElements = root.getChildren("university");
			root.removeContent();
			for (DBConnVO vo : voList) { // ele: university
				//List<Element> children = ele.getChildren();
				
				Element university = new Element("university");
				root.addContent(university);
				Element isRoute = new Element("isRoute");
				Element servers = new Element("servers");
				for (Method voMethod:vo.getClass().getMethods()) { // e: name, dbtype, ... , isRoute
					String methodName = voMethod.getName();
					if (methodName.startsWith("get")) {
						System.out.println(methodName);
						if (voMethod.getParameterTypes().length>0) {
							continue;
						}
						Object content = voMethod.invoke(vo);
						String eleName = methodName.substring(3).toLowerCase();
						if (content instanceof String) {
							System.out.println("(String)");
							university.addContent(new Element(eleName).setText((String) content));
						}else if (content instanceof Boolean) {	//isroute
							System.out.println("(Boolean)");
							isRoute.setAttribute("value", String.valueOf(content));
							university.addContent(isRoute);
						}else if (content instanceof List<?>) {	//servers
							System.out.println("(List)");
							if (null!=content) {
								isRoute.addContent(servers);
								for(Object sVo:(List<?>)content){
									Element server = new Element("server");
									servers.addContent(server);
									for(Method sVoMethod:((Server)sVo).getClass().getMethods()){
										String sMethodName = sVoMethod.getName();
										if (sMethodName.startsWith("get")) {
											System.out.println(sMethodName);
											Object sContent = sVoMethod.invoke(sVo);
											String sEleName = sMethodName.substring(3).toLowerCase();
											if (sContent instanceof String) {
												server.addContent(new Element(sEleName).setText((String) sContent));
											}else if(sContent instanceof Integer){
												server.addContent(new Element(sEleName).setText(sContent.toString()));
											}else {
												System.out.println("Server: unknown field!");
											}
										}
									}
								}
							}
						}else {
							System.out.println("DBConnVO: unknown field!");
						}
					}
				}
			}
		      saveDOM(jdomDoc, filePath);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
	}
	
	private static void saveDOM(org.jdom2.Document doc, String fileName) {
		XMLOutputter XMLOut = new XMLOutputter();  
		OutputStream os = null;
	     try {
				String path = System.getProperty("user.dir") + "/config/" + fileName;
				File file = new File(path);
				org.jdom2.output.Format newFormat = org.jdom2.output.Format.getCompactFormat();
				newFormat.setIndent("\t");
				newFormat.setEncoding("ISO-8859-1");
				os = new FileOutputStream(file);
				XMLOut.setFormat(newFormat);
				XMLOut.output(doc, os);
	  } catch (FileNotFoundException e) {
	   e.printStackTrace();
	  } catch (IOException e) {
	   e.printStackTrace();
	  }
	}

	private static org.jdom2.Document useDOMParser(String fileName)
			throws ParserConfigurationException, SAXException, IOException {
		// creating DOM Document
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder;
		dBuilder = dbFactory.newDocumentBuilder();
		InputStream is = null;
		try {
			String path = System.getProperty("user.dir") + "/config/" + fileName;
			File file = new File(path);
			is = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Document doc = dBuilder.parse(is);
		DOMBuilder domBuilder = new DOMBuilder();
		
		return domBuilder.build(doc);

	}

	public static String[] getNames(List<DBConnVO> dbConnVOs) {
		String[] result = new String[dbConnVOs.size()];
		int i = 0;
		for (DBConnVO dbConnVO : dbConnVOs) {
			try {
				result[i] = unicodeDecode(dbConnVO.getName());
				i++;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	public static String unicodeDecode(String theString) {
		char aChar;
		int len = theString.length();
		StringBuffer outBuffer = new StringBuffer(len);
		for (int x = 0; x < len;) {
			aChar = theString.charAt(x++);
			if (aChar == '\\') {
				aChar = theString.charAt(x++);

				if (aChar == 'u') {
					// Read the xxxx
					int value = 0;
					for (int i = 0; i < 4; i++) {
						aChar = theString.charAt(x++);
						switch (aChar) {
						case '0':
						case '1':
						case '2':
						case '3':
						case '4':
						case '5':
						case '6':
						case '7':
						case '8':
						case '9':
							value = (value << 4) + aChar - '0';
							break;
						case 'a':
						case 'b':
						case 'c':
						case 'd':
						case 'e':
						case 'f':
							value = (value << 4) + 10 + aChar - 'a';
							break;
						case 'A':
						case 'B':
						case 'C':
						case 'D':
						case 'E':
						case 'F':
							value = (value << 4) + 10 + aChar - 'A';
							break;
						default:
							throw new IllegalArgumentException("Malformed   \\uxxxx   encoding.");
						}
					}
					outBuffer.append((char) value);
				} else {
					if (aChar == 't')
						aChar = '\t';
					else if (aChar == 'r')
						aChar = '\r';
					else if (aChar == 'n')
						aChar = '\n';
					else if (aChar == 'f')
						aChar = '\f';
					outBuffer.append(aChar);
				}
			} else
				outBuffer.append(aChar);
		}
		return outBuffer.toString();
	}

	public static void main(String[] args) {
		String filePath = "conns_config.xml";
		List<DBConnVO> reslist = XmlParser.generateDBVOList(filePath);

		try {
			for (DBConnVO dbvo : reslist) {
				System.out.println(unicodeDecode(dbvo.getName()));
				if (null != dbvo.getServers() && !dbvo.getServers().isEmpty()) {
					System.out.println("host: "+dbvo.getServer(0).getHost());
					System.out.println("host: "+dbvo.getServer(1).getHost());
				}
			}
			updateFromDBVOList("newconfig.xml", reslist);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
